#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/iterable/concept/range_concepts.h>
#include <vex/iterable/stl_interop.h>
#include <vex/iterable/any_range.h>
#include <vex/iterable/make_any_range.h>

#include <vex.tests/mstest/log_message.h>
#include <vex.tests/mstest/crt_mem_check.h>
#include <vex.tests/mstest/tests.range/range_archetype.h>
#include <vex.tests/mstest/tests.core/tests.core.handle_policy.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace range_any_range_concept_check {

    using namespace boost::assign;

    TEST_CLASS(range_any_range_concept_check)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.any.concept_check")
        END_TEST_CLASS_ATTRIBUTE();
    public:
        typedef range_archetype<> trivial_range_archetype;
        typedef range_archetype<vex::output_tag, int const&> output_range_archetype;
        typedef range_archetype<vex::one_pass_tag, int&> one_pass_range_archetype;
        typedef range_archetype<vex::forward_tag, int&> forward_range_archetype;
        typedef range_archetype<vex::bidirectional_tag, int&> bidirectional_range_archetype;
        typedef range_archetype<vex::random_access_tag, int&> random_access_range_archetype;

        typedef vex::any_range<> any_trivial_range;
        typedef vex::any_range<vex::output_tag, int const&> any_output_range;
        typedef vex::any_range<vex::one_pass_tag, int&> any_one_pass_range;
        typedef vex::any_range<vex::forward_tag, int&> any_forward_range;
        typedef vex::any_range<vex::bidirectional_tag, int&> any_bidirectional_range;
        typedef vex::any_range<vex::random_access_tag, int&> any_random_access_range;

        TEST_METHOD(any_range_satisfies_respective_range_concepts)
        {
            {
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange< 
                    vex::any_range<> >));
            }

            {
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::OutputRange< 
                    vex::any_range<vex::output_tag, int>, int >));
            }

            {
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange< 
                    vex::any_range< vex::one_pass_tag, int&> >));
            }

            {
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange< 
                    vex::any_range< vex::forward_tag, int&> >));
            }

            {
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange< 
                    vex::any_range< vex::bidirectional_tag, int&>
                >));
            }

            {
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<
                    vex::any_range< vex::random_access_tag, int&>
                >));
            }
        }

        TEST_METHOD(any_range_implements_handle_policy)
        {
            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                any_trivial_range r1, r2;
                // l-value, l_value
                handle_policy::correctly_implements_handle_policy(r1, r2);
                // r-value, r-value
                handle_policy::correctly_implements_handle_policy(
                    vex::make_any_range(r1),
                    vex::make_any_range(r2));
                // l-value, r-value
                handle_policy::correctly_implements_handle_policy(
                    r1,
                    vex::make_any_range(r2));
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                any_one_pass_range r1, r2;
                handle_policy::correctly_implements_handle_policy(r1, r2);
                handle_policy::correctly_implements_handle_policy(
                    vex::make_any_range(r1),
                    vex::make_any_range(r2));
                handle_policy::correctly_implements_handle_policy(
                    r1,
                    vex::make_any_range(r2));
            }
            
            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                any_output_range r1, r2;
                handle_policy::correctly_implements_handle_policy(r1, r2);
                handle_policy::correctly_implements_handle_policy(
                    vex::make_any_range(r1),
                    vex::make_any_range(r2));
                handle_policy::correctly_implements_handle_policy(
                    r1,
                    vex::make_any_range(r2));
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                any_forward_range r1, r2;
                handle_policy::correctly_implements_handle_policy(r1, r2);
                handle_policy::correctly_implements_handle_policy(
                    vex::make_any_range(r1),
                    vex::make_any_range(r2));
                handle_policy::correctly_implements_handle_policy(
                    r1,
                    vex::make_any_range(r2));
            }
            
            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                any_bidirectional_range r1, r2;
                handle_policy::correctly_implements_handle_policy(r1, r2);
                handle_policy::correctly_implements_handle_policy(
                    vex::make_any_range(r1),
                    vex::make_any_range(r2));
                handle_policy::correctly_implements_handle_policy(
                    r1,
                    vex::make_any_range(r2));
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                any_random_access_range r1, r2;
                handle_policy::correctly_implements_handle_policy(r1, r2);
                handle_policy::correctly_implements_handle_policy(
                    vex::make_any_range(r1),
                    vex::make_any_range(r2));
                handle_policy::correctly_implements_handle_policy(
                    r1,
                    vex::make_any_range(r2));
            }
        }

        TEST_METHOD(is_range_is_true_for_ranges_and_false_otherwise)
        {
            Assert::IsFalse(vex::iterable::is_range< std::vector<int> >::value);
            Assert::IsFalse(vex::iterable::is_range< std::list<int> >::value);
#if _MSC_VER == 1800 && BOOST_VERSION == 105500
#pragma message(__FILE__ " boost::container::slist does not compile with boost 105500 and MSC 1800")
#else
            Assert::IsFalse(vex::iterable::is_range< boost::container::slist<int> >::value);
#endif
            Assert::IsTrue(vex::iterable::is_range< trivial_range_archetype >::value);
            Assert::IsTrue(vex::iterable::is_range< random_access_range_archetype >::value);
            Assert::IsTrue(vex::iterable::is_range< any_trivial_range >::value);
            Assert::IsTrue(vex::iterable::is_range< any_random_access_range >::value);
        }

        TEST_METHOD(any_range_is_constructible_from_models_of_ranges_instances)
        {
            {
                typedef any_trivial_range exp_range_t;
                Assert::IsTrue(std::is_same<
                    exp_range_t, 
                    vex::iterable::any::any_range_from_range<trivial_range_archetype>::type
                >::value, wlog_message() 
                << "expected: " << typeid(exp_range_t).name() << std::endl 
                << "got: " 
                << typeid(vex::iterable::any::any_range_from_range<trivial_range_archetype>::type).name() 
                | as_string);
            }

            {
                typedef any_output_range exp_range_t;
                Assert::IsTrue(std::is_same<
                    exp_range_t, 
                    vex::iterable::any::any_range_from_range<output_range_archetype>::type
                >::value);
            }

            {
                typedef any_one_pass_range exp_range_t;
                Assert::IsTrue(std::is_same<
                    exp_range_t, 
                    vex::iterable::any::any_range_from_range<one_pass_range_archetype>::type
                >::value);
            }

            {
                typedef any_forward_range exp_range_t;
                Assert::IsTrue(std::is_same<
                    exp_range_t, 
                    vex::iterable::any::any_range_from_range<forward_range_archetype>::type
                >::value);
            }

            {
                typedef any_bidirectional_range exp_range_t;
                Assert::IsTrue(std::is_same<
                    exp_range_t, 
                    vex::iterable::any::any_range_from_range<bidirectional_range_archetype>::type
                >::value);
            }

            {
                typedef any_random_access_range exp_range_t;
                Assert::IsTrue(std::is_same<
                    exp_range_t, 
                    vex::iterable::any::any_range_from_range<random_access_range_archetype>::type
                >::value);
            }
        }

        TEST_METHOD(make_any_range_from_container_creates_expected_range_types)
        {
            {
                typedef std::vector<int> container_t;
                typedef vex::any_range<vex::random_access_tag, int&> exp_any_range_t;
                typedef decltype(vex::make_any_range(std::declval< container_t& >())) act_any_range_t;
                
                Assert::IsTrue(std::is_same<exp_any_range_t, act_any_range_t>::value);

                BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<act_any_range_t>));
            }

            {
                typedef std::list<int> container_t;
                typedef vex::any_range<vex::bidirectional_tag, int&> exp_any_range_t;
                typedef decltype(vex::make_any_range(std::declval< container_t& >())) act_any_range_t;

                Assert::IsTrue(std::is_same<exp_any_range_t, act_any_range_t>::value);

                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<act_any_range_t>));
            }

#if _MSC_VER == 1800 && BOOST_VERSION == 105500
#pragma message(__FILE__ " boost::container::slist does not compile with boost 105500 and MSC 1800")
            Assert::Fail(wlog_message()
                << __FILE__
                << L" boost::container::slist does not compile with boost 105500 and MSC 1800"
                | as_string);
#else
            {
                typedef boost::container::slist<int> container_t;
                typedef vex::any_range<vex::forward_tag, int&> exp_any_range_t;
                typedef decltype(vex::make_any_range(std::declval< container_t& >())) act_any_range_t;

                Assert::IsTrue(std::is_same<exp_any_range_t, act_any_range_t>::value);

                BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<act_any_range_t>));
            }
#endif
        }

        TEST_METHOD(result_of_make_any_range_from_trivial_range_has_expected_traits)
        {
            typedef any_trivial_range range_t;
            typedef decltype(vex::make_any_range(
                std::declval<trivial_range_archetype>())) result_range_t;

            BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<range_t>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<result_range_t>));

            typedef vex::iterable::range_traits<result_range_t> result_range_traits;
            typedef vex::iterable::range_traits<trivial_range_archetype> archetype_range_traits;

            Assert::IsTrue(std::is_same<
                result_range_traits::category, archetype_range_traits::category
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::reference, archetype_range_traits::reference
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::value_type, archetype_range_traits::value_type
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::pointer, archetype_range_traits::pointer
            >::value);
        }

        TEST_METHOD(any_range_is_constructible_from_range_l_values)
        { // from l-value archetype:
            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                trivial_range_archetype a;
                Assert::IsTrue(vex::iterable::is_range<decltype(a)>::value);
                auto r = vex::make_any_range(a);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                output_range_archetype a;
                Assert::IsTrue(vex::iterable::is_range<decltype(a)>::value);
                auto r = vex::make_any_range(a);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::OutputRange<decltype(r), int>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                one_pass_range_archetype a;
                Assert::IsTrue(vex::iterable::is_range<decltype(a)>::value);
                auto r = vex::make_any_range(a);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                r.front();
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                forward_range_archetype a;
                Assert::IsTrue(vex::iterable::is_range<decltype(a)>::value);
                auto r = vex::make_any_range(a);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                r.front();
                Assert::IsTrue(r.save());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                bidirectional_range_archetype a;
                Assert::IsTrue(vex::iterable::is_range<decltype(a)>::value);
                auto r = vex::make_any_range(a);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                Assert::IsFalse(r.move_back());
                r.front();
                r.back();
                Assert::IsTrue(r.save());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                random_access_range_archetype a;
                Assert::IsTrue(vex::iterable::is_range<decltype(a)>::value);
                auto r = vex::make_any_range(a);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                Assert::IsFalse(r.move_back());
                Assert::IsFalse(r.move_front_of(0u));
                Assert::IsFalse(r.move_back_of(0u));
                r.at(0u);
                Assert::AreEqual(std::size_t(0u), r.size());
                r.front();
                r.back();
                Assert::IsTrue(r.save());
            }
        }

        TEST_METHOD(any_range_is_constructible_from_range_r_values)
        { // from r-value archetype:
            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                auto r = vex::make_any_range(trivial_range_archetype());
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                auto r = vex::make_any_range(output_range_archetype());
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::OutputRange<decltype(r), int>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                Assert::IsFalse(r.put(42));
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                auto r = vex::make_any_range(one_pass_range_archetype());
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                r.front();
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                auto r = vex::make_any_range(forward_range_archetype());
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                r.front();
                Assert::IsTrue(r.save());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                auto r = vex::make_any_range(bidirectional_range_archetype());
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                Assert::IsFalse(r.move_back());
                r.front();
                r.back();
                Assert::IsTrue(r.save());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                auto r = vex::make_any_range(random_access_range_archetype());
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
                Assert::IsFalse(r.move_back());
                Assert::IsFalse(r.move_front_of(0u));
                Assert::IsFalse(r.move_back_of(0u));
                r.at(0u);
                Assert::AreEqual(std::size_t(0u), r.size());
                r.front();
                r.back();
                Assert::IsTrue(r.save());
            }
        }

        TEST_METHOD(any_range_is_constructible_from_container_or_array_l_value)
        { 
            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                std::vector<int> c;
                c += 1, 2, 3, 4;
                
                Assert::IsFalse(vex::iterable::is_range<decltype(c)>::value);
                auto r = vex::make_any_range(c);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::AreEqual(1, r.at(0u));
                Assert::AreEqual(std::size_t(4u), r.size());
                Assert::IsTrue(r.move_front());
                Assert::AreEqual(std::size_t(4u), r.size());
                Assert::IsTrue(r.move_back());
                Assert::AreEqual(std::size_t(3u), r.size());
                Assert::IsTrue(r.move_front_of(1u));
                Assert::AreEqual(std::size_t(2u), r.size());
                Assert::IsTrue(r.move_back_of(1u));
                Assert::AreEqual(std::size_t(1u), r.size());
                Assert::AreEqual(2, r.front());
                Assert::AreEqual(2, r.back());
                Assert::IsTrue(r.save());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                int c[3] = { 1, 2, 3 };
                Assert::IsTrue(std::is_array<decltype(c)>::value);
                Assert::IsFalse(vex::iterable::is_range<decltype(c)>::value);
                auto r = vex::make_any_range(c);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsTrue(r.move_front());
            }

            {
                VEX_TESTS_SCOPED_MEM_CHECK();
                std::list<int> c;
                
                Assert::IsFalse(vex::iterable::is_range<decltype(c)>::value);
                auto r = vex::make_any_range(c);
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<decltype(r)>));
                Assert::IsTrue(r);
                Assert::IsFalse(r.move_front());
            }
        }

        TEST_METHOD(result_of_make_any_range_from_output_range_has_expected_traits)
        {
            typedef any_output_range range_t;
            typedef decltype(vex::make_any_range(
                std::declval<output_range_archetype>())) result_range_t;

            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OutputRange<range_t, int>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OutputRange<result_range_t, int>));

            typedef vex::iterable::range_traits<result_range_t> result_range_traits;
            typedef vex::iterable::range_traits<output_range_archetype> archetype_range_traits;

            Assert::IsTrue(std::is_same<
                result_range_traits::category, archetype_range_traits::category
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::reference, archetype_range_traits::reference
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::value_type, archetype_range_traits::value_type
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::pointer, archetype_range_traits::pointer
            >::value);
        }

        TEST_METHOD(result_of_make_any_range_from_one_pass_range_has_expected_traits)
        {
            typedef any_one_pass_range range_t;
            typedef decltype(vex::make_any_range(
                std::declval<one_pass_range_archetype>())) result_range_t;

            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<range_t>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<result_range_t>));

            typedef vex::iterable::range_traits<result_range_t> result_range_traits;
            typedef vex::iterable::range_traits<one_pass_range_archetype> archetype_range_traits;

            Assert::IsTrue(std::is_same<
                result_range_traits::category, archetype_range_traits::category
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::reference, archetype_range_traits::reference
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::value_type, archetype_range_traits::value_type
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::pointer, archetype_range_traits::pointer
            >::value);
        }

        TEST_METHOD(result_of_make_any_range_from_forward_range_has_expected_traits)
        {
            typedef any_forward_range range_t;
            typedef decltype(vex::make_any_range(
                std::declval<forward_range_archetype>())) result_range_t;

            BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<range_t>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<result_range_t>));

            typedef vex::iterable::range_traits<result_range_t> result_range_traits;
            typedef vex::iterable::range_traits<forward_range_archetype> archetype_range_traits;

            Assert::IsTrue(std::is_same<
                result_range_traits::category, archetype_range_traits::category
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::reference, archetype_range_traits::reference
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::value_type, archetype_range_traits::value_type
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::pointer, archetype_range_traits::pointer
            >::value);
        }

        TEST_METHOD(result_of_make_any_range_from_bidirectional_range_has_expected_traits)
        {
            typedef any_bidirectional_range range_t;
            typedef decltype(vex::make_any_range(
                std::declval<bidirectional_range_archetype>())) result_range_t;

            BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<range_t>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<result_range_t>));

            typedef vex::iterable::range_traits<result_range_t> result_range_traits;
            typedef vex::iterable::range_traits<bidirectional_range_archetype> archetype_range_traits;

            Assert::IsTrue(std::is_same<
                result_range_traits::category, archetype_range_traits::category
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::reference, archetype_range_traits::reference
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::value_type, archetype_range_traits::value_type
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::pointer, archetype_range_traits::pointer
            >::value);
        }

        TEST_METHOD(result_of_make_any_range_from_random_access_range_has_expected_traits)
        {
            typedef any_random_access_range range_t;
            typedef decltype(vex::make_any_range(
                std::declval<random_access_range_archetype>())) result_range_t;

            BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<range_t>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<result_range_t>));

            typedef vex::iterable::range_traits<result_range_t> result_range_traits;
            typedef vex::iterable::range_traits<random_access_range_archetype> archetype_range_traits;

            Assert::IsTrue(std::is_same<
                result_range_traits::category, archetype_range_traits::category
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::reference, archetype_range_traits::reference
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::value_type, archetype_range_traits::value_type
            >::value);
            Assert::IsTrue(std::is_same<
                result_range_traits::pointer, archetype_range_traits::pointer
            >::value);
        }
    };
}}